<template>
  <div class="container">
    <div :id="container" class="spec"></div>
  </div>
</template>

<script>
    import colorMap from './color_map'

    export default {
        name: 'Spectrogram',
        props: {
            container: {
                type: String,
                default: 'spectrogram'
            },
            spectrogramData: {
                type: Array,
                default: () => []
            }
        },
        watch: {
            spectrogramData: {
                handler (val) {
                    this.values = val
                    this.loadSpec(val)
                },
                immediate: true,
                deep: true
            }
        },
        data: function () {
            return {
                colors: [],
                fftSamples: 512,
                sampleRate: 16000,
                values: []
            }
        },
        methods: {
            loadLabels (
                height,
                bgFill,
                fontSizeFreq,
                fontSizeUnit,
                fontType,
                textColorFreq,
                textColorUnit,
                textAlign,
                container,
                cvs
            ) {
                bgFill = bgFill || 'rgba(68,68,68,0)'
                fontSizeFreq = fontSizeFreq || '12px'
                fontSizeUnit = fontSizeUnit || '10px'
                fontType = fontType || 'Helvetica'
                textColorFreq = textColorFreq || '#fff'
                textColorUnit = textColorUnit || '#fff'
                textAlign = textAlign || 'center'
                container = container || '#specLabels'
                const bgWidth = 55
                const getMaxY = height
                const labelIndex = 5 * (getMaxY / 256)
                const freqStart = 0
                const step = (this.sampleRate / 2 - freqStart) / labelIndex

                // prepare canvas element for labels
                const ctx = cvs.getContext('2d')
                cvs.height = height
                cvs.width = bgWidth

                if (ctx) {
                    // fill background
                    ctx.fillStyle = bgFill
                    ctx.fillRect(0, 0, bgWidth, getMaxY)
                    ctx.fill()
                    let i

                    // render labels
                    for (i = 0; i <= labelIndex; i++) {
                        ctx.textAlign = textAlign
                        ctx.textBaseline = 'middle'

                        const freq = freqStart + step * i
                        // const index = Math.round((freq / (this.sampleRate / 2)) * this.fftSamples);
                        const label = freq >= 1000 ? (freq / 1000).toFixed(1) : Math.round(freq)
                        const units = freq >= 1000 ? 'KHz' : 'Hz'
                        const yLabelOffset = 2
                        const x = 16
                        let y

                        if (i === 0) {
                            y = getMaxY + i - 5
                            // unit label
                            ctx.fillStyle = textColorUnit
                            ctx.font = fontSizeUnit + ' ' + fontType
                            ctx.fillText(units, x + 24, y)
                            // freq label
                            ctx.fillStyle = textColorFreq
                            ctx.font = fontSizeFreq + ' ' + fontType
                            ctx.fillText(label, x, y)
                        } else {
                            y = getMaxY - i * 50 + yLabelOffset
                            // unit label
                            ctx.fillStyle = textColorUnit
                            ctx.font = fontSizeUnit + ' ' + fontType
                            ctx.fillText(units, x + 24, y)
                            // freq label
                            ctx.fillStyle = textColorFreq
                            ctx.font = fontSizeFreq + ' ' + fontType
                            ctx.fillText(label, x, y)
                        }
                    }
                }
            },
            loadSpec (yData) {
                const start = new Date().getTime()
                const that = this
                if (yData.length === 0) {
                    return
                }
                // container
                const spectrogram = document.getElementById(this.container)// 获取元素
                spectrogram.innerHTML = ''
                // spectrogram
                const canvas = document.createElement('canvas')
                canvas.width = spectrogram.offsetWidth
                canvas.height = spectrogram.offsetHeight
                const cvs = spectrogram.appendChild(canvas)
                const ctx = cvs.getContext('2d') // 创建上下文 context 对象
                const cvsWidth = cvs.width
                const cvsHeight = cvs.height
                // lable
                const label = document.createElement('canvas')
                label.style.left = '0'
                label.style.top = '0'
                label.style.position = 'absolute'
                label.style.zIndex = '9'
                label.style.height = cvsHeight + 'px'
                label.style.width = '55px'
                spectrogram.appendChild(label)

                this.loadLabels(
                    canvas.height,
                    'rgba(68,68,68,0.5)',
                    '12px',
                    '10px',
                    '',
                    '#fff',
                    '#f7f7f7',
                    'center',
                    '#specLabels',
                    label
                )

                const step = cvsWidth / yData.length
                const colors = that.colors

                for (let i = 0; i < yData.length; i++) {
                    for (let j = 0; j < yData[i].length; j++) {
                        ctx.strokeStyle = colors[yData[i][j]]
                        ctx.strokeRect(step * i, cvsHeight - j * (cvsHeight / 256), step, cvsHeight / 256)
                    }
                }
                const end = new Date().getTime()
                console.log('耗时：' + (end - start) + ' ms')
            },
            getColors () {
                const colors = []
                for (let i = 0; i < colorMap.length; i++) {
                    const color = 'rgb(' + colorMap[i][0] * 255 + ',' + colorMap[i][1] * 255 + ',' + colorMap[i][2] * 255 + ',' + colorMap[i][3] * 255 + ')'
                    colors.push(color)
                }
                this.colors = colors
            }
        },
        mounted () {
            const that = this
            that.getColors()
            that.loadSpec(that.values)
            that.$nextTick(() => {
                window.addEventListener('resize', () => {
                    that.loadSpec(that.values)
                })
            })
        }
    }
</script>

<style scoped>
    .container {
        width: 100%;
        height: 100%;
    }

    .spec {
        width: 100%;
        height: 100%;
        margin: 0 auto;
        position: relative;
    }
</style>
